/**
 * Copyright (c) 2005 Frank Toennies
 * Redistributions in source code form must reproduce the above copyright and this condition.
 * The contents of this file are subject to the Sun Project JXTA License Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * A copy of the License is available at http://www.jxta.org/jxta_license.html.
 * Please see the license information page for instructions on use of the license in source files.
 *
 * This software consists of voluntary contributions made by many individuals on behalf of Project JXTA.
 * For more information on Project JXTA, please see http://www.jxta.org.
 *
 */
package net.jxta.myjxta.ui;

import info.clearthought.layout.TableLayout;
import net.jxta.myjxta.View;
import net.jxta.myjxta.plugin.ISelectableNode;
import net.jxta.myjxta.plugin.PluginContainer;
import net.jxta.myjxta.plugin.SelectionNodeType;
import net.jxta.myjxta.presence.PeerEntry;
import net.jxta.myjxta.presence.PeerStatus;
import net.jxta.myjxta.search.SearchContext;
import net.jxta.myjxta.search.Searcher;
import net.jxta.myjxta.ui.action.PeerPingAction;
import net.jxta.myjxta.util.Group;
import net.jxta.myjxta.util.GroupListener;
import net.jxta.myjxta.util.Resources;
import net.jxta.myjxta.util.objectmodel.JxtaNode;
import net.jxta.myjxta.util.objectmodel.PeerNode;
import net.jxta.rendezvous.RendezVousService;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import java.util.ResourceBundle;


/**
 * This Dialog Panel shows a information / navigation table of all known peers (the truth is:command dialog pipes)
 * inside the current jxta group. Each ro inside the table represents one pipe. A QuickSearch Textfield enables
 * the user to search for nodes and shares. next version of this panel will also support the listing
 * of shares inside the current group)
 *
 * @author Frank Toennies --
 */
public final class SingleGroupNavigationPanel extends JPanel implements PluginContainer.ISelectionProvider {

    private JTable m_groupNavigationTable = null;
    private static final boolean ENABLE_STATUS_CONTROLS = true;
    private SingleGroupTableModel m_groupTableModel = null;
    private final View m_view;
    private static final ResourceBundle STRINGS = Resources.getStrings();

    private static final ImageIcon g_peerPresentIcon = Resources.getInstance().getIconResource("TreeTable.PeerPresent");
    private static final ImageIcon g_peerAbsendIcon = Resources.getInstance().getIconResource("TreeTable.PeerAbsent");
    private final JComboBox m_quickSearchContext;
    private final JTextField m_quickSearch;
    private final JScrollPane m_scrollPane;
    private JPanel m_infoPanel;

    /**
     * @param view
     * @param group
     */
    public SingleGroupNavigationPanel(View view, Group group) {
        m_groupNavigationTable = new JTable();
        m_view = view;
        m_view.getControl().getPluginContainer().registerSelectionProvider(this);
        m_groupNavigationTable.setDefaultRenderer(PeerEntry.class, new MyTableCellRenderer());
        m_groupTableModel = new SingleGroupTableModel(group);
        m_groupNavigationTable.setModel(m_groupTableModel);
        int rowHeight = 20/*g_peerPresentIcon != null ? g_peerPresentIcon.getIconHeight() + 3 : 20*/;
        m_groupNavigationTable.setRowHeight(rowHeight);
        m_groupNavigationTable.setShowGrid(false);

        m_groupNavigationTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        m_groupNavigationTable.addMouseListener(new MyTableMouseListener());
        m_groupNavigationTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                MenuManager.getInstance(m_view).updateMenuStatus();
            }
        });


        m_quickSearchContext = new JComboBox(new DefaultComboBoxModel(new Object[]{
                new SearchContext(STRINGS.getString("label.pipe.name"),
                        Searcher.PIPE),
//            new SearchContext(STRINGS.getString("label.share.name"),
//                Searcher.SHARE)
        }));

        m_quickSearch = new JTextField(25);
        m_quickSearch.addKeyListener(new MyJXTAView.QuickSearchKeyAdapter(m_quickSearchContext, m_view));


        double size[][];
        if (ENABLE_STATUS_CONTROLS) {
            m_infoPanel = createInfoPanel();
            size = new double[][]{{0.00, 0.50, 0.50, 0.00}, //columns x
                    {25, TableLayout.FILL, 25}}; //rows y          //reserve 20 pixel above the table for some checkboxes (not there yet)
        } else {
            size = new double[][]{{0.00, 0.50, 0.50, 0.00}, //columns x
                    {0, TableLayout.FILL, 25}}; //rows y          //reserve 20 pixel above the table for some checkboxes (not there yet)
        }

        setLayout(new TableLayout(size));
        JScrollPane sp = new JScrollPane(m_groupNavigationTable);
        m_scrollPane = sp;
        m_scrollPane.getViewport().setOpaque(false);
        sp.setBackground(m_groupNavigationTable.getBackground());

        add(sp, "1, 1, 2, 1");    //cell 1,1 towards 2,1 (first one is column or X cord)

        add(m_quickSearchContext, "1,2");  //add the context combo to the outermost south west cell (row 3, column 1)
        add(m_quickSearch, "2,2"); //add the textfield right beside the combo

        if (ENABLE_STATUS_CONTROLS) {
            add(m_infoPanel, "0,0,2,0");
        }
    }

    private PeerNode[] getAllPeerNodes() {
        return getTableModel().getAllPeerNodes();
    }

    public void pingAllNodes() {
        final PeerNode[] nodes = getAllPeerNodes();
        new PeerPingAction("not used", m_view) {
            protected PeerNode[] getNodes() {
                return nodes;
            }
        }.actionPerformed(null);
    }

    private JPanel createInfoPanel() {
        JPanel panel = new JPanel();

        double size[][] =
                {{0.25, 0.25, TableLayout.FILL, 60}, //columns x
                        {TableLayout.FILL}}; //rows y          //reserve 20 pixel above the table for some checkboxes (not there yet)

        panel.setLayout(new TableLayout(size));

        ImageIcon iconConnected = Resources.getInstance().getIconResource("GroupTable.connectedToRdv");
        ImageIcon iconNotConnected = Resources.getInstance().getIconResource("GroupTable.notConnectedToRdv");
        ImageIcon iconIsRdv = Resources.getInstance().getIconResource("GroupTable.isRdv");

        Group group = getGroup();
        Group parentGroup = group.getParentGroup();

        final GroupStatusControl parentGroupControl = new GroupStatusControl(parentGroup.getName(), iconConnected, iconNotConnected, iconIsRdv);
        parentGroupControl.setToolTipText("network connection state");
        final GroupStatusControl ownGroupControl = new GroupStatusControl(group.getName(), iconConnected, iconNotConnected, iconIsRdv);
        ownGroupControl.setEnabled(true);

        MyGroupListener groupStatusControl = new MyGroupListener(group, parentGroupControl, ownGroupControl);
        group.addListener(groupStatusControl);
        parentGroup.addListener(groupStatusControl);
        groupStatusControl.updateControl(group, ownGroupControl);
        groupStatusControl.updateControl(parentGroup, parentGroupControl);

//        Group parentGroup = getGroup().getParentGroup();
//        GroupStatusControl parentGroupStatusControl=null;
//        if (parentGroup !=null){
//            parentGroupStatusControl=new GroupStatusControl("npgConnection",iconConnected,iconNotConnected);
//            parentGroupStatusControl.setActive(parentGroup.isConnected());
//            parentGroup.addListener(new MyGroupListener(parentGroupStatusControl,null));
//        }

        JPanel iconPanel = new JPanel();
        iconPanel.add(parentGroupControl);

        //quick and dirty... remove this one after we have a better place to activate it
        parentGroupControl.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    pingAllNodes();
                }
            }


        });

        ownGroupControl.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                int modifiers = e.getModifiersEx();
                if (e.getClickCount() == 2 && (modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {

                    RendezVousService rendezVousService = m_groupTableModel.getGroup().getPeerGroup().getRendezVousService();
                    if (m_groupTableModel.getGroup().isOwnPeerRdv()) {
                        rendezVousService.stopRendezVous();
                    } else {
                        //hidden promote to rdv.... maybe not a good idea so dont advertise it too much...
                        //may be removed at any time
                        rendezVousService.startRendezVous();
                    }
                }
            }
        });

        iconPanel.add(ownGroupControl);
        panel.add(iconPanel, "3,0,3,0");

//nano: not yet working (sometimes the wrong state is shown... maybe we are missing a rdv-connect event?)
//        JPanel parentGroupPanel=new JPanel();
//        parentGroupPanel.add(parentGroupStatusControl);
//        panel.add(parentGroupPanel,"0,0,0,0");
        return panel;
    }

    private final class GroupStatusControl extends JPanel {
        private final Icon m_edgeConnected;
        private final Icon m_edgeNotConnected;
        //private boolean m_active = false;
        private Icon m_activeIcon;
        private final ImageIcon m_isRdvIcon;
        private final String m_groupName;

        public void setEdgeConnected(boolean p_connected) {
            m_activeIcon = p_connected ? m_edgeConnected : m_edgeNotConnected;
            setToolTipText("Own peer a Edge peer for group " + m_groupName + " and " + (p_connected ? "IS " : "IS NOT ") + "connected");
            invalidate();
            repaint();
        }

        public void setIsRdv() {
            m_activeIcon = m_isRdvIcon;
            setToolTipText("Own peer is a Rendezvous for group " + m_groupName);
            invalidate();
            repaint();
        }

        public GroupStatusControl(String groupName, ImageIcon p_edgeConnected, ImageIcon p_edgeNotConnected, ImageIcon p_isRdv) {
            m_edgeConnected = p_edgeConnected;
            m_edgeNotConnected = p_edgeNotConnected;
            m_isRdvIcon = p_isRdv;
            m_activeIcon = m_edgeNotConnected;
            m_groupName = groupName;

//            m_button=new JButton(m_edgeNotConnected);
//            m_button.setEnabled(false);
//            add(m_button);
        }

        public Dimension getPreferredSize() {
            return new Dimension(m_edgeConnected.getIconWidth(), m_edgeConnected.getIconHeight());
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (isEnabled() && m_activeIcon != null) {
                m_activeIcon.paintIcon(this, g, 0, 0);
            }
        }
    }

    public SingleGroupTableModel getTableModel() {
        return (SingleGroupTableModel) m_groupNavigationTable.getModel();
    }


    /**
     * @return
     */
    public Group getGroup() {
        return m_groupTableModel.getGroup();
    }


    private class MyTableCellRenderer extends DefaultTableCellRenderer {


        /**
         * Returns the default table cell renderer.
         *
         * @param table      the <code>JTable</code>
         * @param value      the value to assign to the cell at
         *                   <code>[row, column]</code>
         * @param isSelected true if cell is selected
         * @param hasFocus   true if cell has focus
         * @param row        the row of the cell to render
         * @param column     the column of the cell to render
         * @return the default table cell renderer
         */
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            String label;
            if (value instanceof PeerEntry) {
                PeerEntry peerEntry = (PeerEntry) value;
                setIcon(peerEntry.getIcon(isSelected, hasFocus));
                long secs = System.currentTimeMillis() - peerEntry.getLastSeen();
                secs = secs / 1000;
                label = peerEntry.m_peer.getName();

                //todo localise
                StringBuffer tooltipText = new StringBuffer().append("<html>").append(label).append(" was last seen ").append(secs).append(" sec ago");

                tooltipText.append("<br>Reverse discovery status: ");
                if (Boolean.TRUE.equals(peerEntry.getNode().getReverseDiscoveryState()))
                    tooltipText.append("true");
                else if (Boolean.FALSE.equals(peerEntry.getNode().getReverseDiscoveryState()))
                    tooltipText.append("false");
                else
                    tooltipText.append("unknown");

                tooltipText.append("<br>Online status: ");
                PeerStatus presenceStatus = peerEntry.getNode().getPresenceStatus();
                if (presenceStatus == null) {
                    presenceStatus = PeerStatus.getUnknownState();
                    peerEntry.getNode().setInfo(PeerNode.STATUS_PRESENCE, presenceStatus, false);
                }

                tooltipText.append(presenceStatus.getState()).append(" / ").append(presenceStatus.getStatusMessage());


                tooltipText.append("</html>");

                setToolTipText(tooltipText.toString());

                if (peerEntry.getNode().isConnecting()) {
                    label = label + " connecting...";
                } else if (presenceStatus.isAway()) {
                    label = label + " (Away)";
                } else if (presenceStatus.isOffline()) {
                    label = label + " (Offline)";
                } else if (peerEntry.getNode().wasReachable() == Boolean.FALSE) {
                    label = label + " (Unreachable)";
                }

                //label=label+" " + secs + peerEntry+":"+peerEntry.getNode();
            } else {
                label = value.toString();
            }

            return super.getTableCellRendererComponent(table, label, isSelected, hasFocus, row, column);
        }


    }

    /**
     * called before the component is removed from the navigation panel
     */
    public void shutdown() {
        m_groupTableModel.shutdown();
        m_view.getControl().getPluginContainer().removeSelectionProvider(this);
    }


    /**
     * Mouse Listener of the Table - responsible for the doubleclick behaviour and the triggering of the
     * context menu
     */
    private class MyTableMouseListener extends MouseAdapter {
        /**
         * Invoked when a mouse button has been pressed on a component.
         */
        public void mousePressed(MouseEvent e) {
            processMouseEvent(e);
        }

        /**
         * Invoked when a mouse button has been released on a component.
         */
        public void mouseReleased(MouseEvent e) {
            processMouseEvent(e);
        }

        private void processMouseEvent(MouseEvent e) {
            if (e.isPopupTrigger()) {
                JTable table = (JTable) e.getSource();
                int row = table.rowAtPoint(e.getPoint());
                if (!table.getSelectionModel().isSelectedIndex(row)) {
                    table.getSelectionModel().setSelectionInterval(row, row);
                }

                triggerGlobalMyJxtaPopup(e);
            }
        }

        /**
         * Invoked when the mouse has been clicked on a component.
         */
        public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() > 1) {
                doubleClick(e);
            }

        }

        private void doubleClick(MouseEvent p_e) {
            JTable table = (JTable) p_e.getSource();
            int row = table.rowAtPoint(p_e.getPoint());
            int column = table.columnAtPoint(p_e.getPoint());
            PeerEntry value = (PeerEntry) table.getValueAt(row, column);
            doubleClick(p_e, value.getNode());
        }

        private void doubleClick(MouseEvent p_e, PeerNode p_peer) {

//            if (p_peer.getPeer().getPipeAdvertisement().getPipeID().equals(getGroup().getOwnPeersCommandId())) {
//                return; // no self-chat!
//            }
            triggerGlobalMyJxtaPopup(p_e);

//            Peer peer = p_peer.getPeer();
//          MyPeerChatAction myPeerChatAction = new MyPeerChatAction(peer.getName(), m_view, p_peer);
//        if (myPeerChatAction.isEnabled()) {
//          myPeerChatAction.actionPerformed(null);
//    }

        }

        private void triggerGlobalMyJxtaPopup(MouseEvent p_e) {
            Object source = p_e.getSource();
            JComponent jComponent = null;
            //normally the JTable should be the source
            if (source instanceof JComponent) {
                jComponent = (JComponent) source;
            } else {
                source = SingleGroupNavigationPanel.this.m_scrollPane;
            }
            m_view.getControl().getPluginContainer().triggerGlobalMyJxtaPopup(getSelectedNodes(), p_e, jComponent);
        }
    }

    public ISelectableNode[] getSelectedNodes() {
        final int rows[] = m_groupNavigationTable.getSelectedRows();
        if (rows.length == 0) {
            return null;
        }
        ISelectableNode[] result = new ISelectableNode[rows.length];

        for (int i = 0; i < rows.length; i++) {
            final int finalRow = rows[i];

            result[i] = new ISelectableNode() {
                public JxtaNode getJxtaNode() {

                    PeerEntry peerEntry = (PeerEntry) m_groupNavigationTable.getValueAt(finalRow, 0);
                    if (peerEntry != null) {
                        return peerEntry.getNode();
                    } else {
                        System.out.println("");
                        return null;
                    }

                }

                public SelectionNodeType getType() {
                    return SelectionNodeType.Peer;
                }
            };

        }

//        System.out.println("return:"+result[0].getJxtaNode());
        return result;
    }


    private static final class MyGroupListener implements GroupListener {
        private final GroupStatusControl m_parentGroupStatusControl;
        private final GroupStatusControl m_ownGroupStatusControl;
        private final Group m_ownGroup;

        public MyGroupListener(Group group, GroupStatusControl p_rdvConnectionControl, GroupStatusControl p_ownGroupControl) {
            m_parentGroupStatusControl = p_rdvConnectionControl;
            m_ownGroupStatusControl = p_ownGroupControl;
            m_ownGroup = group;
            updateControl(group.getParentGroup(), m_parentGroupStatusControl);
            updateControl(group, m_ownGroupStatusControl);
        }

        public void groupEvent(Group g, String s, EventObject p_re) {
            if (g.equals(m_ownGroup)) {
                eventGroup(g.getParentGroup(), m_parentGroupStatusControl);
                eventGroup(g, m_ownGroupStatusControl);
            } else {
                eventGroup(g, m_parentGroupStatusControl);
            }
        }

        private void eventGroup(Group g, GroupStatusControl p_control) {
            updateControl(g, p_control);
        }

        private void updateControl(Group g, GroupStatusControl p_control) {
            if (g.isConnected()) {

                boolean ownPeerRdv = g.isOwnPeerRdv();
                if (ownPeerRdv) {
                    p_control.setIsRdv();
                } else {
                    p_control.setEdgeConnected(true);
                }
            } else {
                p_control.setEdgeConnected(false);
            }
        }
    }
}